//
// BSDF: Bidirectional Scattering Distribution Function 
// =
// BRDF: Bidirectional Reflection Distribution Function 
// +
// BTDF: Bidirectional Transmission Distribution Function 
//
#ifndef CSUPBR_BSDF_H
#define CSUPBR_BSDF_H

#include "pbrmath.h"

namespace csupbr {

    struct Fresnel {
        virtual Spectrum evaluate(Float cosThetaT) const = 0;
    };

    struct FresnelDielectric : Fresnel {
        FresnelDielectric(Float eta, Float etaT) : eta(eta), etaT(etaT) {}

        Spectrum evaluate(Float cosThetaT) const override {
            return FrDielectric(cosThetaT, eta, etaT);
        }
        Float eta, etaT;
    };

    enum BSDFType {
        BSDF_REFLECTION = 1 << 0,
        BSDF_TRANSMISSION = 1 << 1,

        BSDF_DIFFUSE = 1 << 3,
        BSDF_GLOSSY = 1 << 4,
        BSDF_SPECULAR = 1 << 5,
        BSDF_ALL = BSDF_DIFFUSE | BSDF_GLOSSY | BSDF_SPECULAR |
                   BSDF_REFLECTION | BSDF_TRANSMISSION,
    };

    struct Intersection;

    struct BSDFRecord {
        BSDFRecord(Intersection &isct) : isct(isct) {}
        Intersection& isct;
        Vec3f wo, wi;
    };

    /**
     * Bidirectional Scattering Distribution Function 
     */
    struct BSDF {
        BSDF(BSDFType type)
            : type(type) {}
        virtual ~BSDF() {}

        // BSDF Sampling
        virtual Spectrum sample(BSDFRecord& bsdfRec, const Point2f &sample, Float &pdf) const = 0;

        // Light Sampling
        virtual Spectrum evaluate(BSDFRecord& bsdfRec, Float &pdf) const = 0;

        virtual Float pdf(const BSDFRecord& bsdfRec) const = 0;

        const BSDFType type;
    };

    // Absolute Diffuse
    struct LambertianReflection : BSDF {
        LambertianReflection()
            : BSDF(BSDFType(BSDFType::BSDF_REFLECTION | BSDFType::BSDF_DIFFUSE)) {}
        ~LambertianReflection() override {}
        Spectrum sample(BSDFRecord& bsdfRec, const Point2f &sample, Float &pdf) const override;
        Spectrum evaluate(BSDFRecord& bsdfRec, Float &pdf) const override;
        Float pdf(const BSDFRecord& bsdfRec) const override;
    };

    // Absolute Reflection
    struct SpecularReflection : BSDF {
        SpecularReflection(float roughness = .0f)
            : BSDF(BSDFType(BSDFType::BSDF_REFLECTION | BSDFType::BSDF_SPECULAR)),
              roughness(clamp(roughness, 0, 1.f)) {}
        ~SpecularReflection() override {}
        Spectrum sample(BSDFRecord& bsdfRec, const Point2f &sample, Float &pdf) const override;
        Spectrum evaluate(BSDFRecord& bsdfRec, Float &pdf) const override;
        Float pdf(const BSDFRecord& bsdfRec) const override;

        float roughness;
    };

    // Fresnel Specular
    struct SpecularTransmission : BSDF {
        SpecularTransmission(float refract_rate)
            : BSDF(BSDFType(BSDFType::BSDF_TRANSMISSION | BSDFType::BSDF_SPECULAR)),
              refract_rate(refract_rate), fresnel(1.f, refract_rate) {}
        ~SpecularTransmission() override {}
        Spectrum sample(BSDFRecord& bsdfRec, const Point2f &sample, Float &pdf) const override;
        Spectrum evaluate(BSDFRecord& bsdfRec, Float &pdf) const override;
        Float pdf(const BSDFRecord& bsdfRec) const override;

        float refract_rate;
        FresnelDielectric fresnel;
    };

}

#endif // !CSUPBR_BSDF_H